
const Service = require("egg").Service;
const Minio = require("minio");
//node.js 文件操作对象
const fs = require('fs');
//node.js 路径操作对象
const path = require('path');

const UUID = require("uuid");
const { error } = require("console");
const { resolve } = require("path");


class Dfs extends Service {
  async connect() {
    const { app, ctx } = this
    // console.log(123, app.config)
    let { host, port, appKey, appSecret } = app.config.Rpc.clients.filestore
    let minioClient = new Minio.Client({
      endPoint: host,
      port: port,
      useSSL: false,
      accessKey: appKey,
      secretKey: appSecret,
    });
    // console.log('ok')
    return minioClient
  }
  async listBuckets() {
    const { app, ctx } = this
    try {
      const minioClient = await this.connect()
      let data = await minioClient.listBuckets()
      return data
    }
    catch (err) {
      throw err
    }
  }
  async bucketExists(name) {
    const { app, ctx } = this
    try {
      const minioClient = await this.connect()
      const err = app.validator.validate({ name: 'string' }, { name });
      if (err) {
        let msg = '操作失败：msg:' + err.map(e => {
          return e.field;
        }).join() + '字段校验失败'
        throw new Error(msg)
      }
      let isExists = await minioClient.bucketExists(name).then(res => {
        // console.log(111, res)
        return res
      })
        .catch(err => {
          throw new Error(err)
        })
      if (isExists) {
        // console.log('存在')
        return true
      } else {
        // console.log('不存在')

        throw new Error('不存在！')
      }
    }
    catch (err) {
      throw err
    }
  }

  async putObject() {
    const { app, ctx } = this
    try {
      const { bucket, path: filePath } = ctx.request.body
      const err = app.validator.validate({ bucket: 'string', path: 'string' }, ctx.request.body);
      if (err) {
        let msg = '操作失败：msg:' + err.map(e => {
          return e.field;
        }).join() + '字段校验失败'
        throw new Error(msg)
      }
      let isExist = await this.bucketExists(bucket)
      if (!isExist) throw new Error('桶不存在！！！')
      const file = ctx.request.files[0]
      const fileStream = fs.createReadStream(file.filepath)
      console.log(1212121, file.mimeType)
      const stat = fs.statSync(file.filepath)
      const minioClient = await this.connect()
      let result = await minioClient.putObject(bucket, filePath, fileStream, stat.size, { type: file.mimeType })
        .then(({ etag }) => {
          console.log('Successfully uploaded the stream')
          if (etag) return true

          throw new Error('上传失败！')
        })
        .catch(err => {
          throw new Error(err)
        })
      return result

    }
    catch (err) {
      throw err
    }

  }

  async fPutObject() {
    const { app, ctx } = this
    try {
      const { bucket, path, localPath } = ctx.query
      const err = app.validator.validate({ bucket: 'string', path: 'string', localPath: 'string' }, ctx.query);
      if (err) {
        let msg = '操作失败：msg:' + err.map(e => {
          return e.field;
        }).join() + '字段校验失败'
        throw new Error(msg)
      }

      let isExist = await this.bucketExists(bucket)
      if (!isExist) throw new Error('目标桶不存在！！！')

      let fileExist = fs.existsSync(localPath)
      if (!fileExist) throw new Error('本地文件不存在！！！')

      const minioClient = await this.connect()
      let result = await minioClient.fPutObject(bucket, path, localPath)
        .then(({ etag }) => {
          if (etag) return true
          // return false
          throw new Error('上传失败！')
        })
        .catch(err => {
          throw new Error(err)
        })
      return result
    }
    catch (err) {
      throw err
    }
  }

  async copyObject() {
    const { app, ctx } = this
    try {
      const { newBucket, newPath, sourceBucket, sourcePath } = ctx.query
      const err = app.validator.validate({ newBucket: 'string', newPath: 'string', sourceBucket: 'string', sourcePath: 'string', }, ctx.query);
      if (err) {
        let msg = '操作失败：msg:' + err.map(e => {
          return e.field;
        }).join() + '字段校验失败'
        throw new Error(msg)
      }

      let isExist = await this.bucketExists(newBucket)
      if (!isExist) throw new Error('目标桶不存在！！！')

      let sourceExist = await this.bucketExists(sourceBucket)
      if (!sourceExist) throw new Error('源桶不存在！！！')

      const minioClient = await this.connect()

      let copyConditions = new Minio.CopyConditions()
      copyConditions.setMatchETag('bd891862ea3e22c93ed53a098218791d')
      let result = await minioClient.copyObject(sourceBucket, this.getObjectPath(sourcePath), newBucket, this.getObjectPath(newPath), copyConditions)
        .then(res => {
          console.log(res)
          return res
        })
        .catch(err => {
          throw new Error(err)
        })
      return result
    }
    catch (err) {
      console.log(err)
      throw err
    }

  }

  async statObject(bucket, path) {
    const { app, ctx } = this
    try {
      let isExist = await this.bucketExists(bucket)
      if (!isExist) throw new Error('目标桶不存在！！！')

      const minioClient = await this.connect()
      let result = await minioClient.statObject(bucket, path)
        .then(res => {
          if (res.etag) return true
          throw new Error('文件不存在！！！')
        })
        .catch(err => {
          throw new Error(err)
        })
      return result
    }
    catch (err) {

      throw err
    }
  }

  async removeObject() {
    const { app, ctx } = this
    try {
      const { bucket, path: bPath } = ctx.query
      const err = app.validator.validate({ bucket: 'string', path: 'string' }, ctx.query);
      if (err) {
        let msg = '操作失败：msg:' + err.map(e => {
          return e.field;
        }).join() + '字段校验失败'
        throw new Error(msg)
      }
      const isExist = await this.statObject(bucket, bPath)
      if (!isExist) throw new Error('文件不存在！！！')
      const minioClient = await this.connect()
      let result = await minioClient.removeObject(bucket, bPath)
        .then(res => {
          return true
        })
        .catch(err => {
          throw new Error(err)
        })
      return result
    }
    catch (err) {
      throw err
    }

  }

  async listObjects() {
    const { app, ctx } = this
    try {
      const { bucket, path: bPath } = ctx.query
      const err = app.validator.validate({ bucket: 'string' }, ctx.query);
      if (err) {
        let msg = '操作失败：msg:' + err.map(e => {
          return e.field;
        }).join() + '字段校验失败'
        throw new Error(msg)
      }

      let isExist = await this.bucketExists(bucket)
      if (!isExist) throw new Error('目标桶不存在！！！')

      let res = await new Promise(async (resolve, reject) => {
        const minioClient = await this.connect()
        let objectsStream = await minioClient.listObjects(bucket, bPath || '', true, {})
        let data = []
        await objectsStream.on('data', function (obj) {
          data.push(obj)
        })
        await objectsStream.on('end', () => {
          // console.log(data)
          resolve(data)
        })
        await objectsStream.on('error', function (e) {
          reject(e)
        })
      })
        .then(data => {
          return data
        })
        .catch(err => {
          throw new Error(err)
        })
      return res

    }
    catch (err) {
      throw err
    }
  }

  async getObject() {
    const { app, ctx } = this
    try {
      const { bucket, path: bPath } = ctx.params
      const err = app.validator.validate({ bucket: 'string', path: 'string' }, ctx.params);
      if (err) {
        let msg = '操作失败：msg:' + err.map(e => {
          return e.field;
        }).join() + '字段校验失败'
        throw new Error(msg)
      }

      let isExist = await this.bucketExists(bucket)
      if (!isExist) throw new Error('目标桶不存在！！！')

      const minioClient = await this.connect()

      let result = await minioClient.statObject(bucket, bPath)
      let { type, 'content-type': ctype } = result.metaData

      let dataStream = await new Promise((resolve, reject) => {
        minioClient.getObject(bucket, bPath, function (e, dataStream) {
          if (err) {
            reject(err)
          }
          let buffers = []
          dataStream.on('data', function (chunk) {
            buffers.push(chunk)
          })
          dataStream.on('end', function () {
            resolve(Buffer.concat(buffers))
          })
          dataStream.on('error', function (err) {
            reject(err)
          })
        })
      }).then(res => {
        return res
      })
        .catch(err => {
          throw new Error(err)
        })


      let index = bPath.lastIndexOf('.')
      if (index <= 0) throw new Error('文件名称格式有误！')
      let hz = bPath.slice(index + 1)

      return { data: dataStream, fileName: this.getfileName(bPath), fileType: type || ctype, hz }
    }
    catch (err) {
      throw err
    }
  }

  async getPresinedUrl() {
    const { app, ctx } = this
    try {
      const { bucket, path: bPath, duration } = ctx.query
      const err = app.validator.validate({ bucket: 'string', path: 'string' }, ctx.query);
      if (err) {
        let msg = '操作失败：msg:' + err.map(e => {
          return e.field;
        }).join() + '字段校验失败'
        throw new Error(msg)
      }

      let isExist = await this.bucketExists(bucket)
      if (!isExist) throw new Error('目标桶不存在！！！')

      const minioClient = await this.connect()
      //
      let result = await minioClient.presignedGetObject(bucket, bPath, duration || 7 * 60 * 60 * 24)
        .then(res => {
          return res
        })
        .catch(err => {
          throw new Error(err)
        })
      return result
    }
    catch (err) {
      throw err
    }
  }

  getfileName(name) {
    if (name) {
      let index = name.lastIndexOf('/')
      if (index != -1) return name.slice(index + 1)
      return name
    }
  }
  getObjectPath(path) {
    if (path.slice(0, 1) == '/') return path
    else return '/' + path
  }
}

module.exports = Dfs;